﻿ #region Disclaimer/Info
 
 /////////////////////////////////////////////////////////////////////////////////////////////////
 //
 //   File:		RepositoryBase.cs
 //   Website:		http://dexterblogengine.com/
 //   Authors:		http://dexterblogengine.com/About.ashx
 //   Rev:		1
 //   Created:		19/01/2011
 //   Last edit:		19/01/2011
 //   License:		GNU Library General Public License (LGPL)
 // 
 //   For updated news and information please visit http://dexterblogengine.com/
 //   Dexter is hosted to Codeplex at http://dexterblogengine.codeplex.com
 //   For any question contact info@dexterblogengine.com
 //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Dexter.Core.Data.Repository;
using Dexter.Domain.EventArgs;
using Dexter.Domain.Model;
using Dexter.Logger.Concretes;
using Dexter.Logger.Contracts;
using NHibernate;
using NHibernate.Linq;
using Topics.Radical.Validation;
using ISessionFactory = Dexter.Repository.NHibernate.SessionFactory.ISessionFactory;

namespace Dexter.Repository.NHibernate.Repositories {
	/// <summary>
	/// 	Rapresents a repository base of domain objects.
	/// </summary>
	/// <typeparam name = "T">The type of the domain model.</typeparam>
	public abstract class RepositoryBase <T> : IRepository <T> where T : EntityBase {
		Type concreteType;

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "RepositoryBase&lt;T&gt;" /> class.
		/// </summary>
		/// <param name = "sessionFactory">The session factory.</param>
		/// <param name = "logger">The logger.</param>
		protected RepositoryBase ( ISessionFactory sessionFactory , ILogger logger ) {
			SessionFactory = sessionFactory;
			Logger = logger ?? NullLogger.Instance;
		}

		/// <summary>
		/// 	Gets or sets the session factory.
		/// </summary>
		/// <value>The session factory.</value>
		protected ISessionFactory SessionFactory { get; private set; }

		/// <summary>
		/// 	Gets or sets the type of the concrete.
		/// </summary>
		/// <value>The type of the concrete.</value>
		protected Type ConcreteType {
			get { return concreteType ?? typeof ( T ); }
			set { concreteType = value; }
		}

		/// <summary>
		/// 	Gets a value indicating whether [use cache].
		/// </summary>
		/// <value><c>true</c> if [use cache]; otherwise, <c>false</c>.</value>
		protected bool UseCache {
			get { return true; }
		}

		/// <summary>
		/// 	Gets the session.
		/// </summary>
		/// <value>The session.</value>
		protected ISession Session {
			get { return SessionFactory.GetSession ( ); }
		}

		/// <summary>
		/// 	Gets or sets the logger.
		/// </summary>
		/// <value>The logger.</value>
		protected ILogger Logger { get; private set; }

		#region Implementation of IQueryable

		/// <summary>
		/// 	Gets the expression tree that is associated with the instance of <see cref = "T:System.Linq.IQueryable" />.
		/// </summary>
		/// <value></value>
		/// <returns>The <see cref = "T:System.Linq.Expressions.Expression" /> that is associated with this instance of <see cref = "T:System.Linq.IQueryable" />.</returns>
		public virtual Expression Expression {
			get { return Session.Query <T> ( ).Expression; }
		}

		/// <summary>
		/// 	Gets the type of the element(s) that are returned when the expression tree associated with this instance of <see cref = "T:System.Linq.IQueryable" /> is executed.
		/// </summary>
		/// <value></value>
		/// <returns>A <see cref = "T:System.Type" /> that represents the type of the element(s) that are returned when the expression tree associated with this object is executed.</returns>
		public virtual Type ElementType {
			get { return Session.Query <T> ( ).ElementType; }
		}

		/// <summary>
		/// 	Gets the query provider that is associated with this data source.
		/// </summary>
		/// <value></value>
		/// <returns>The <see cref = "T:System.Linq.IQueryProvider" /> that is associated with this data source.</returns>
		public virtual IQueryProvider Provider {
			get { return Session.Query <T> ( ).Provider; }
		}

		#endregion

		#region Implementation of ICollection

		/// <summary>
		/// 	Adds the specified item.
		/// </summary>
		/// <param name = "item">The item.</param>
		public virtual void Add ( T item ) {
			Ensure.That ( item )
				.Named ( "item" )
				.WithPreview ( ( v , e ) => Logger.Warn ( v.GetFullErrorMessage ( ) , e ) )
				.IsNotNull ( )
				.WithMessage ( "Invalid item" );

			//if ( !Session.Query <T> ( ).Contains ( item ) ) {
			if (item.IsTransient) {
				Session.Save ( item );

				InstanceAdded.Raise ( this , new InstanceAddedEventArgs <T> ( item ) );
			}
		}

		/// <summary>
		/// 	Clears this instance.
		/// </summary>
		public virtual void Clear ( ) {
			Session.Delete ( String.Format ( "from {0}" , ConcreteType.Name ) );
		}

		/// <summary>
		/// 	Determines whether [contains] [the specified item].
		/// </summary>
		/// <param name = "item">The item.</param>
		/// <returns>
		/// 	<c>true</c> if [contains] [the specified item]; otherwise, <c>false</c>.
		/// </returns>
		public virtual bool Contains ( T item ) {
			Ensure.That ( item )
				.Named ( "item" )
				.WithPreview ( ( v , e ) => Logger.Warn ( v.GetFullErrorMessage ( ) , e ) )
				.IsNotNull ( )
				.WithMessage ( "Invalid item" );

			return Session.Query <T> ( ).Contains ( item );
		}

		/// <summary>
		/// 	Copies to.
		/// </summary>
		/// <param name = "array">The array.</param>
		/// <param name = "arrayIndex">Index of the array.</param>
		public virtual void CopyTo ( T[] array , int arrayIndex ) {
			Ensure.That ( array )
				.Named ( "item" )
				.WithPreview ( ( v , e ) => Logger.Warn ( v.GetFullErrorMessage ( ) , e ) )
				.IsNotNull ( )
				.WithMessage ( "Invalid array" );

			Ensure.That ( arrayIndex )
				.Named ( "arrayIndex" )
				.WithPreview ( ( v , e ) => Logger.Warn ( v.GetFullErrorMessage ( ) , e ) )
				.IsGreaterThen ( 0 , Or.Equal )
				.WithMessage ( "Invalid arrayIndex" );

			throw new NotSupportedException ( );
		}

		/// <summary>
		/// 	Gets the count.
		/// </summary>
		/// <value>The count.</value>
		public virtual int Count {
			get { return Session.Query <T> ( ).Count ( ); }
		}

		/// <summary>
		/// 	Gets a value indicating whether this instance is read only.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is read only; otherwise, <c>false</c>.
		/// </value>
		public virtual bool IsReadOnly {
			get { return false; }
		}

		/// <summary>
		/// 	Removes the specified item.
		/// </summary>
		/// <param name = "item">The item.</param>
		/// <returns></returns>
		public virtual bool Remove ( T item ) {
			Ensure.That ( item )
				.Named ( "item" )
				.WithPreview ( ( v , e ) => Logger.Warn ( v.GetFullErrorMessage ( ) , e ) )
				.IsNotNull ( )
				.WithMessage ( "Invalid item" );

			Session.Delete ( item );
			return true;
		}

		/// <summary>
		/// 	Gets the enumerator.
		/// </summary>
		/// <returns></returns>
		public virtual IEnumerator <T> GetEnumerator ( ) {
			return Session.Query <T> ( ).AsEnumerable ( ).GetEnumerator ( );
		}

		/// <summary>
		/// 	Returns an enumerator that iterates through a collection.
		/// </summary>
		/// <returns>
		/// 	An <see cref = "T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
		/// </returns>
		IEnumerator IEnumerable.GetEnumerator ( ) {
			return GetEnumerator ( );
		}

		#endregion

		#region Implementation of IRepository

		/// <summary>
		/// 	Includes the specified sub selector.
		/// </summary>
		/// <param name = "subSelector">The sub selector.</param>
		/// <returns></returns>
		public virtual IQueryable <T> Include ( Expression <Func <T , object>> subSelector ) {
			return Include ( subSelector , false );
		}

		/// <summary>
		/// 	Includes the specified sub selector.
		/// </summary>
		/// <param name = "subSelector">The sub selector.</param>
		/// <param name = "useCache">if set to <c>true</c> [use cache].</param>
		/// <returns></returns>
		public virtual IQueryable <T> Include ( Expression <Func <T , object>> subSelector , bool useCache ) {
			return Include ( subSelector , false , null );
		}

		/// <summary>
		/// 	Includes the specified sub selector.
		/// </summary>
		/// <param name = "subSelector">The sub selector.</param>
		/// <param name = "useCache">if set to <c>true</c> [use cache].</param>
		/// <param name = "regionName">Name of the region.</param>
		/// <returns></returns>
		public virtual IQueryable <T> Include ( Expression <Func <T , object>> subSelector , bool useCache , string regionName ) {
			INhFetchRequest <T , object> q = Session.Query <T> ( ).Fetch ( subSelector );

			if ( useCache ) {
				q.Cacheable ( );

				if ( !regionName.IsNullOrEmpty ( ) ) {
					q.CacheRegion ( regionName );
				}
			}

			return q;
		}

		/// <summary>
		/// 	Clears the cache region.
		/// </summary>
		/// <param name = "regionName">Name of the region.</param>
		public virtual void ClearCacheRegion ( string regionName ) {
			Ensure.That ( regionName )
				.Named ( "regionName" )
				.WithPreview ( ( v , e ) => Logger.Warn ( v.GetFullErrorMessage ( ) , e ) )
				.IsNotNullNorEmpty ( )
				.WithMessage ( "Invalid region name" );

			Session.SessionFactory.EvictQueries ( regionName );
		}

		#endregion

		/// <summary>
		/// 	Raised when a customer is placed into the repository.
		/// </summary>
		public event EventHandler <InstanceAddedEventArgs <T>> InstanceAdded;
	}
}
